# -*- coding: utf-8 -*-
# @Time : 2024/2/2 10:41
# @Author : ZH
# @File : streetscape.py
# @Software: PyCharm
import json
import re
from typing import List, Optional

import requests
from pydantic import BaseModel


class Panorama(BaseModel):
    """
    设置返回值model
    """
    pano_id: str
    lat: float
    lon: float
    heading: float
    pitch: Optional[float]
    roll: Optional[float]
    date: Optional[str]
    height: Optional[float]


class StreetScape:
    """
    获取街景id
    输入一个四角坐标范围，然后根据固定步长进行处理
    """
    def __init__(self, *args, **kwargs):
        print('-' * 10)
        print('天王盖地虎，宝塔镇河妖')
        print('-' * 10)

    def make_search_url(self, lat: float, lon: float) -> str:
        """
        传入经纬度，针对当前点生成查询街景的js请求链接
        :param lat:
        :param lon:
        :return:
        """
        url = (
            "https://maps.googleapis.com/maps/api/js/"
            "GeoPhotoService.SingleImageSearch"
            "?pb=!1m5!1sapiv3!5sUS!11m2!1m1!1b0!2m4!1m2!3d{0:}!4d{1:}!2d200"
            "!3m17!2m1!1sen!9m1!1e2!11m12!1m3!1e2!2b1!3e2!1m3!1e3!2b1!3e2"
            "!1m3!1e10!2b1!3e2!4m6!1e1!1e2!1e3!1e4!1e8!1e6&callback=_xdc_._null"
        )
        return url.format(lat, lon)

    def parse_search_url(self, url: str) -> tuple:
        """
        提取url中的经纬度
        :param url:
        :return:
        """
        # Define a regular expression pattern to match latitude and longitude in the URL
        pattern = r"!3d([\d.]+)!.+?4d([\d.]+)"

        # Use re.search to find the first match in the URL
        match = re.search(pattern, url)

        # If a match is found, extract latitude and longitude
        if match:
            lat = float(match.group(1))
            lon = float(match.group(2))
            return lat, lon
        else:
            # Return None if no match is found
            return None, None

    def extract_panoramas(self, text: str) -> List[Panorama]:
        """
        解析请求回来的js数据文件
        :param text:
        :return:
        """

        # The response is actually javascript code. It's a function with a single
        # input which is a huge deeply nested array of items.
        blob = re.findall(r"_xdc_._null\( (.*) \)$", text)[0]
        data = json.loads(blob)

        if data == [[5, "generic", "Search returned no images."]]:
            return []

        subset = data[1][5][0]
        if not subset[3]:
            return []
        raw_panos = subset[3][0]

        if len(subset) < 9 or subset[8] is None:
            raw_dates = []
        else:
            raw_dates = subset[8]

        # For some reason, dates do not include a date for each panorama.
        # the n dates match the last n panos. Here we flip the arrays
        # so that the 0th pano aligns with the 0th date.
        raw_panos = raw_panos[::-1]
        raw_dates = raw_dates[::-1]

        dates = [f"{d[1][0]}-{d[1][1]:02d}" for d in raw_dates]

        return [
            Panorama(
                pano_id=pano[0][1],  # 设置数据id
                lat=pano[2][0][2],  # 纬度
                lon=pano[2][0][3],  # 经度
                heading=pano[2][2][0] if len(pano[2]) >= 2 else None,
                pitch=pano[2][2][1] if len(pano[2]) >= 2 and len(pano[2][2]) >= 2 else None,
                roll=pano[2][2][2] if len(pano[2]) >= 2 and len(pano[2][2]) >= 3 else None,
                date=dates[i] if i < len(dates) else None,  # 时间
                height=pano[2][1][0] if len(pano[2]) >= 2 and pano[2][1] else None,
            )
            for i, pano in enumerate(raw_panos)
        ]

    def search_panoramas(self, lat: float, lon: float) -> List[Panorama]:
        """
        调用查询样例
        :param lat:
        :param lon:
        :return:
        """

        resp = self.search_request(lat, lon)
        pans = self.extract_panoramas(resp.text)
        return pans

    def search_request(self, lat: float, lon: float, **kwargs):
        """
        url请求函数，可以传入requests的部分参数，但是建议使用自己配置的url
        :param lat:
        :param lon:
        :param kwargs:
        :return:
        """
        url = self.make_search_url(lat, lon)
        if kwargs.get('requests_dict'):
            requests_dict = kwargs.get('requests_dict')
        else:
            requests_dict = {}
        return requests.get(url, **requests_dict)

    def range_segmentation(self, original_coordinates: List[dict], square_side_length: float = 0.0005000000000023874):
        """
        将original_coordinates表示的四边形拆分为给定边长的小方块。
        :param original_coordinates: 字典列表，其中“lat”和“lng”键表示四个角坐标。
        :param square_side_length: 每个小方块的边长。
        :return: 字典列表，其中“lat”和“lng”键表示每个小方块的角坐标。
        """
        # Extracting corner coordinates
        latitudes = [coord["lat"] for coord in original_coordinates]
        longitudes = [coord["lng"] for coord in original_coordinates]

        # Sorting coordinates to handle different order scenarios
        min_lat, max_lat = min(latitudes), max(latitudes)
        min_lng, max_lng = min(longitudes), max(longitudes)

        # Calculate the number of squares in x and y directions
        num_squares_x = int((max_lng - min_lng) / square_side_length) + 1
        num_squares_y = int((max_lat - min_lat) / square_side_length) + 1

        # Initialize the list to store the corner coordinates of small squares
        small_squares = []

        # Generate the corner coordinates for each small square
        for i in range(num_squares_y):
            for j in range(num_squares_x):
                square_min_lat = min_lat + i * square_side_length
                square_max_lat = min_lat + (i + 1) * square_side_length
                square_min_lng = min_lng + j * square_side_length
                square_max_lng = min_lng + (j + 1) * square_side_length

                # Ensure the last square covers the original quadrilateral
                if square_max_lng > 180:
                    square_max_lng = 180
                if square_max_lat > 90:
                    square_max_lat = 90
                if square_min_lat < -90:
                    square_min_lat = -90
                if square_max_lng < -180:
                    square_max_lng = -180


                small_squares.append(
                    # {'lat': (square_max_lat + square_min_lat) / 2, 'lng': (square_min_lng + square_max_lng) / 2}
                    {"lat": square_min_lat, "lng": square_min_lng}
                )

        return small_squares

# 使用demo
# if __name__ == '__main__':
#     z = StreetScape()
#     original_coordinates = [{"lat": 33.58944345580667, "lng": 75.30079178276793},
#                             {"lat": 33.58944345580667, "lng": 75.2944832271649},
#                             {"lat": 33.58479589864367, "lng": 75.2944832271649},
#                             {"lat": 33.58479589864367, "lng": 75.30079178276793}]
#     square_side_length = 0.0005000000000023874
#     # 根据步长获取全部的需要请求位置点
#     all_info = z.range_segmentation(
#         original_coordinates=original_coordinates,
#         square_side_length=square_side_length
#     )
#     headers = {
#         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
#         'Accept-Language': 'zh-CN,zh;q=0.9',
#         'Cache-Control': 'no-cache',
#         'Connection': 'keep-alive',
#         'Pragma': 'no-cache',
#         'Upgrade-Insecure-Requests': '1',
#         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
#     }
#     print(f'任务节点：{len(all_info)}')
#     url = z.make_search_url(lat=24.98362216, lon=121.56218680)
#     print(url)
#     response = requests.get(url, headers=headers,
#                             proxies={'http': 'http://192.168.1.240:7890', 'https': 'http://192.168.1.240:7890'})
#     # 数据解析获取pan
#     pans = z.extract_panoramas(response.text)

